#include "rrrasterizer.h"
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <stdint.h>

RRRasterizer::RRRasterizer()
{
    points_index = 0;
}

float TriangleArea(int xa, int ya, int xb, int yb, int xc, int yc)
{
    return 0.5 * abs((xa - xc) * (yb - yc) - (xb - xc) * (ya - yc));
}

void RRRasterizer::rrTriangleInterpolation(int X, int Y)
{
    RRVertex pa, pb, pc;
    int xa, ya, xb, yb, xc, yc;
    float Xbc, Xac, Xab, abc;
    float a, b, c;
    pa = prim.rrGetVert(current_triangle[0]);
    pb = prim.rrGetVert(current_triangle[1]);
    pc = prim.rrGetVert(current_triangle[2]);
    xa = (int)pa.x; ya = (int)pa.y;
    xb = (int)pb.x; yb = (int)pb.y;
    xc = (int)pc.x; yc = (int)pc.y;
    Xbc = TriangleArea(X, Y, xb, yb, xc, yc);
    Xac = TriangleArea(X, Y, xa, ya, xc, yc);
    Xab = TriangleArea(X, Y, xa, ya, xb, yb);
    abc = TriangleArea(xa, ya, xb, yb, xc, yc);
    a = Xbc / abc;
    b = Xac / abc;
    c = Xab / abc;
    // ただ内挿するだけ。depth考慮に入れてないので後で修正
    for(int i=0; i<4; i++) {
        points[points_index].c[i] = a * pa.c[i] + b * pb.c[i] + c * pc.c[i];
    }
}

void RRRasterizer::rrAddPoint(int X, int Y)
{
    points[points_index].x = X;
    points[points_index].y = Y;
    RRVertex pa, pb;
    if(prim.rrGetType() == RR_LINE) {
        pa = prim.rrGetVert(0);
        pb = prim.rrGetVert(1);
        int xa = (int)pa.x; int ya = (int)pa.y;
        int xb = (int)pb.x; int yb = (int)pb.y;
        float t;
        t = ((float)((X - xa) * (xb - xa) + (Y - ya) * (yb - ya))) / ((xb - xa) * (xb - xa) + (yb - ya) * (yb - ya));
        //float f;
        // ただ内挿するだけ。depth考慮に入れてないので後で修正
        for(int i=0; i<4; i++) {
            //printf("%d %d\n", pb.x, pb.y);
            points[points_index].c[i] = (1 - t) * pa.c[i] + t * pb.c[i];
            //printf("%f\n", points[points_index].color[i]);
        }
    }
    else if(prim.rrGetType() == RR_TRIANGLE) {
        rrTriangleInterpolation(X, Y);
    }
    else if(prim.rrGetType() == RR_POLYGON) {
        rrTriangleInterpolation(X, Y);
    }
    points_index++;
}

void RRRasterizer::rrRast()
{
    //puts("Im in Rast");
    if(prim.rrGetType() == RR_LINE)
        rrLineRast(0, 1);
    else if(prim.rrGetType() == RR_LINE_STRIP)
        rrLineStripRast();
    else if(prim.rrGetType() == RR_LINE_LOOP)
        rrLineLoopRast();
    else if(prim.rrGetType() == RR_TRIANGLE)
        rrTriangleRast(0, 1, 2);
    else if(prim.rrGetType() == RR_POLYGON)
        rrPolygonRast();
}

void RRRasterizer::rrSetPrim(RRPrim &arg)
{
    prim = arg;
    points_index = 0;
}

void RRRasterizer::rrLineRast(int s, int t)
{
    puts("Im in LineRast");
    int x0, y0, x1, y1;
    x0 = (int)prim.rrGetX(s);
    y0 = (int)prim.rrGetY(s);
    x1 = (int)prim.rrGetX(t);
    y1 = (int)prim.rrGetY(t);

    int X, Y;
    X = x0;
    Y = y0;

    int dx, dy;
    dx = x1 - x0;
    dy = y1 - y0;

    int dirX, dirY;		/* 増分方向とその量 */
    dirX = 1;
    if(dx < 0) {
        dirX = -dirX;
        dx = -dx;
    }
    dirY = 1;
    if(dy < 0) {
        dirY = -dirY;
        dy = -dy;
    }

    int d, e, a, b, ax, ay, bx, by; /* 補正の設定 */
    if(dx < dy) {
        d = dy;
        e = -dy;
        a = 2 * dx;
        b = -2 * dy;
        ax = 0;
        ay = dirY;
        bx = dirX;
        by = 0;
    }
    else {
        d = dx;
        e = -dx;
        a = 2 * dy;
        b = -2 * dx;
        ax = dirX;
        ay = 0;
        bx = 0;
        by = dirY;
    }

    for(int i=0; i<=d; i++) {
        rrAddPoint(X, Y);
        X = X + ax;
        Y = Y + ay;
        e = e + a;
        if(e >= 0) {
            X = X + bx;
            Y = Y + by;
            e = e + b;
        }
    }

    //ラスタ座標の表示
    /* for(int i=0; i<points_index; i++) { */
    /*   printf("%d %d\n", points[i].x, points[i].y); */
    /* } */
}

int RRRasterizer::rrGetPointsNum()
{
    return points_index;
}

RRFragment RRRasterizer::rrGetPoint(int index)
{
    return points[index];
}

void RRRasterizer::rrLineStripRast()
{
    int vert_num = prim.rrGetVertsNum();
    for(int i=0; i<vert_num-1; i++) {
        rrLineRast(i, i+1);
    }
}

void RRRasterizer::rrLineLoopRast()
{
    int vert_num = prim.rrGetVertsNum();
    int i;
    for(i=0; i<vert_num-1; i++) {
        rrLineRast(i, i+1);
    }
    rrLineRast(i, 0);
}

template <class T>
inline void swap(T& a, T& b)
{
    T tmp(a);
    a = b;
    b = tmp;
}

void RRRasterizer::rrHLineRast(int x0, int x1, int Y)
{
    if(x1 < x0)
        swap(x0, x1);
    int dx;
    dx = x1 - x0;
    for(int i=0; i<=dx; i++) {
        rrAddPoint(x0 + i, Y);
    }
}

void RRRasterizer::rrTriangleRast(int s, int t, int u)
{
    current_triangle[0] = s;
    current_triangle[1] = t;
    current_triangle[2] = u;

    int x0, y0, x1, y1, x2, y2;
    x0 = (int)prim.rrGetX(s);
    y0 = (int)prim.rrGetY(s);
    x1 = (int)prim.rrGetX(t);
    y1 = (int)prim.rrGetY(t);
    x2 = (int)prim.rrGetX(u);
    y2 = (int)prim.rrGetY(u);

    if(y1 < y0) {
        swap(x1, x0);
        swap(y1, y0);
    }
    if(y2 < y0) {
        swap(x2, x0);
        swap(y2, y0);
    }
    if(y2 < y1) {
        swap(x2, x1);
        swap(y2, y1);
    }

    int Y;
    Y = y0;

    /* 長辺 */
    int dy2, dx2;
    dy2 = y2 - y0;
    if(dy2 <= 0) return;		/* つぶれてる */
    dx2 = x2 - x0;
    int mx2;
    if(dx2 >= 0)
        mx2 = dx2 / dy2;
    else
        mx2 = (dx2 - dy2 + 1) / dy2;
    int ax2, bx2, ex2;
    ax2 = 2 * (mx2 * dy2 - dx2);
    bx2 = 2 * dy2;
    ex2 = dy2;
    int hx2;
    hx2 = x0;

    /* 下半分 */
    int dy1, dx1;
    int mx1;
    int ax1, bx1, ex1;
    int hx1;
    dy1 = y1 - y0;
    if(dy1 > 0) {			/* 下半分がある */
        dx1 = x1 - x0;
        if(dx1 >= 0)
            mx1 = dx1 / dy1;
        else
            mx1 = (dx1 - dy1 + 1) / dy1;
        ax1 = 2 * (mx1 * dy1 - dx1);
        bx1 = 2 * dy1;
        ex1 = dy1;
        hx1 = x0;
        while(dy1 > 0) {
            rrHLineRast(hx1, hx2, Y);
            Y = Y + 1;
            hx2 = hx2 + mx2;
            ex2 = ex2 + ax2;
            if(ex2 <= 0) {
                hx2 = hx2 + 1;
                ex2 = ex2 + bx2;
            }
            hx1 = hx1 + mx1;
            ex1 = ex1 + ax1;
            if(ex1 <= 0) {
                hx1 = hx1 + 1;
                ex1 = ex1 + bx1;
            }
            dy1 = dy1 - 1;
        }
    }

    /* 上半分 */
    dy1 = y2 - y1;
    if(dy1 > 0) {
        dx1 = x2 - x1;
        if(dx1 >= 0)
            mx1 = dx1 / dy1;
        else
            mx1 = (dx1 - dy1 + 1) / dy1;
        ax1 = 2 * (mx1 * dy1 - dx1);
        bx1 = 2 * dy1;
        ex1 = dy1;
        hx1 = x1;
        while(dy1 > 0) {
            rrHLineRast(hx1, hx2, Y);
            Y = Y + 1;
            hx2 = hx2 + mx2;
            ex2 = ex2 + ax2;
            if(ex2 <= 0) {
                hx2 = hx2 + 1;
                ex2 = ex2 + bx2;
            }
            hx1 = hx1 + mx1;
            ex1 = ex1 + ax1;
            if(ex1 <= 0) {
                hx1 = hx1 + 1;
                ex1 = ex1 + bx1;
            }
            dy1 = dy1 - 1;
        }
    }
}

void RRRasterizer::rrPolygonRast()
{
    int verts_num;
    verts_num = prim.rrGetVertsNum();
    //printf("%d\n", verts_num);

    for(int i=0; i<verts_num-2; i++) {
        rrTriangleRast(0, i+1, i+2);
    }
}
